package com.hxxy.wyp.petsLover.utils;

import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


public class MD5Utils {

    private static Logger logger = LoggerFactory.getLogger(MD5Utils.class);
    private static final String ALGORITHM_MD5 = "MD5";
    private static final int M = 1024 * 1024;
    private static final int BLOCK = 50 * M;
    private static final int STREAM_BLOCK = BLOCK*2;
    private static final int CHUNK_SIZE = 2 * M;

    /**
     * 使用md5的算法进行加密
     */
    public static String md5(String plainText) {
        byte[] secretBytes = null;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("没有md5这个算法！");
        }
        String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
        // 如果生成数字未满32位，需要前面补0
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        return md5code;
    }
    public static String md5FileBlocks(File file) {
        RandomAccessFile fs = null;
        try {
            MessageDigest md5 = MessageDigest.getInstance(ALGORITHM_MD5);
            fs = new RandomAccessFile(file, "r");

            if(file.length() > BLOCK) {
                long blocks = file.length() % BLOCK == 0 ? file.length() / BLOCK : file.length() / BLOCK + 1;

                for (int i = 0; i < blocks; i++) {
                    byte[] buffer = new byte[CHUNK_SIZE];

                    fs.seek((long) i * (long) BLOCK);
                    int read = fs.read(buffer, 0, CHUNK_SIZE);

                    md5.update(buffer, 0, read);
                }
                md5.update(String.valueOf(file.length()).getBytes());
            } else {
                int read;
                byte[] buffer = new byte[4096];

                while( (read = fs.read(buffer, 0, buffer.length)) >0 ) {
                    md5.update(buffer, 0, read);
                }
            }

            return encodeHex(md5.digest());
        } catch (Exception e) {
            logger.debug(e.toString());
            return null;
        } finally {
            try {
                if (fs != null)
                    fs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取流md5,并完成文件下载
     * @param ips
     * @param ops
     * @return
     * @throws NoSuchAlgorithmException
     * @throws IOException
     */
    public static String md5FileBlocks(InputStream ips, OutputStream ops) throws NoSuchAlgorithmException,IOException{
        MessageDigest md5 = MessageDigest.getInstance(ALGORITHM_MD5);
        ByteBuffer buffer = ByteBuffer.allocate(STREAM_BLOCK);
        ReadableByteChannel calculateChannel = Channels.newChannel(ips);
        try {
            long fileSize = 0;
            boolean smallFile = true;
            while (calculateChannel.read(buffer) > 0) {
                buffer.flip();
                fileSize+=buffer.remaining();

                if(ops!=null)
                {
                    ops.write(buffer.array(),0,buffer.remaining());
                }

                if(buffer.remaining()<=STREAM_BLOCK/2 && smallFile){
                    md5.update(buffer.array(),0,buffer.remaining());
                }else{
                    smallFile = false;
                    int blocks = 1;
                    if(buffer.remaining() > STREAM_BLOCK/2){
                        blocks++;
                    }
                    for(int i = 0 ;i < blocks ;i ++){
                        md5.update(buffer.array(),i*STREAM_BLOCK/2,CHUNK_SIZE);
                    }
                }
            }
            if(!smallFile){
                md5.update((fileSize+"").getBytes());
            }

        }finally {
            calculateChannel.close();
        }
        return encodeHex(md5.digest());
    }

    public static String md5FileBlocks(InputStream ips) throws NoSuchAlgorithmException,IOException {
        return md5FileBlocks(ips,null);
    }



    public static String md5File(File file, long start, long end) {
        FileInputStream fileInputStream = null;
        try {
            MessageDigest md5 = MessageDigest.getInstance(ALGORITHM_MD5);
            fileInputStream = new FileInputStream(file);
            byte[] buffer = new byte[65536];
            int length;
            if (start > 0)
                fileInputStream.skip(start);
            else
                start = 0;

            long reads = end > 0 ? end - start : file.length() - start;

            while (reads > 0 && (length = fileInputStream.read(buffer)) != -1) {
                int read = length;

                if (reads < length)
                    read = (int) reads;

                md5.update(buffer, 0, read);

                reads -= length;
            }

            return encodeHex(md5.digest());
        } catch (FileNotFoundException e) {
            logger.debug(e.toString());
            return null;
        } catch (Exception e) {
            logger.debug(e.toString());
            return null;
        } finally {
            try {
                if (fileInputStream != null)
                    fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static String md5File(File file) {
        return md5File(file, -1, -1);
    }


    private static byte[] encrypStr(String str, String algorithm) {
        // 加密之后所得字节数组
        byte[] bytes = null;
        try {
            // 获取MD5算法实例 得到一个md5的消息摘要
            MessageDigest md = MessageDigest.getInstance(algorithm);
            //添加要进行计算摘要的信息
            md.update(str.getBytes());
            //得到该摘要
            bytes = md.digest();
        } catch (NoSuchAlgorithmException e) {
            logger.debug(e.toString());
        }
        return null == bytes ? null : bytes;
    }

    private static String encodeHex(byte[] b) {
        return new String(Hex.encodeHex(b)).toUpperCase();
    }
}
